#include "inputfunc.h"
#include "ui_inputfunc.h"
#include <QDebug>
#include <QString>
#include <QKeyEvent>

//初始化五个静态变量，用于帮助实现一个数学表达式解析器
QSet<QString> Inputfunc::num={"0","1","2","3","4","5","6","7","8","9"};
QSet<QString> Inputfunc::xy={"x","pi","e","y"};
QSet<QString> Inputfunc::opt={"+","-","*","/","^"};
QSet<QString> Inputfunc::func={"cos","sin","tan","ln","lg"};

QSet<QString> Inputfunc::num1={"x","i","e",")","y"};

Inputfunc::Inputfunc(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Inputfunc)
{
    ui->setupUi(this);
    ui->input->installEventFilter(this);//事件过滤器,注册监听对象，监听输入的函数表达式
    ui->input->setAttribute(Qt::WA_InputMethodEnabled,false);//禁用输入法输入，强制使用默认输入
    auto List = findChildren<QPushButton*>();//查找所有的QPushButton子对象，并将它们的指针存储在名为List的自动变量中
    brackets=0;//括号计数
    connect(ui->OKbtn,&QPushButton::clicked,this,&Inputfunc::btnClick);
    //按钮循环遍历
    xx=true;
    yy=true;
    for(int i=0; i<List.size(); i++)
    {
        QPushButton*btn=List[i];
        if(btn->text()=="DEL")
            connect(btn,&QPushButton::clicked,this,&Inputfunc::backspace);
        else if(btn->text()=="AC")//清零
            connect(btn,&QPushButton::clicked,this,[=](){
                ui->input->setText("");
                brackets=0;
            });
        else
            connect(btn,&QPushButton::clicked,this,[=](){
                auto* btn = dynamic_cast<QPushButton*>(sender());//此处的sender()指针是指当前点击的按钮对象的指针
                //是一个动态类型转换语句，用于将指向发出信号对象的QObject*类型指针转换为指向QPushButton*类型指针，以便对该对象进行操作
                Filter(btn->text());//传入该按钮上的符号
            });
    }
}

Inputfunc::~Inputfunc()
{
    delete ui;
}

//实现删除操作
void Inputfunc::backspace()
{
    QString te = ui->input->text();
    if(!te.isEmpty())
    {
        switch (te.rbegin()->toLatin1())//将QString转换为一个传统的C风格字符串
        {
            case '=':
                te.remove(te.length()-2,2);
                break;
            case 's'://cos
                te.remove(te.length()-3,3);//表示将text的最后三个字符删除
                break;
            case 'g'://lg
            case 'i'://圆周率
                te.remove(te.length()-2,2);
                break;
            case 'n'://tan sin ln
            {
                if(*(te.cend()-2)=="l")
                    te.remove(te.length()-2,2);
                else
                    te.remove(te.length()-3,3);
                break;
            }
            case '(':
            {
                te.remove(te.length()-1,1);
                brackets--;
                break;
            }
            case ')':
            {
                te.remove(te.length()-1,1);
                brackets++;
                break;
            }
            default://数字
                te.remove(te.length()-1,1);
                break;
        }
        ui->input->setText(te);
    }
}

//用户输入非法字符时，拦截这些字符的输入
//只处理按键事件,与上面的ui->input->installEventFilter(this);对应
bool Inputfunc::eventFilter(QObject *obj, QEvent *e)
{
    if(obj==ui->input)
    {
        if(e->type()==QEvent::KeyPress)//判断事件类型是否为按键按下事件，避免是键盘打入
            return true;
    }
    return QDialog::eventFilter(obj,e);
}

//对用户输入的字符串进行预处理，使其符合计算器的语法规则
void Inputfunc::Filter(const QString& in)
{
    QString cur=ui->input->text();
    QString tail;
    if(!cur.isEmpty())
        tail.push_back(cur.back());//存储cur的顶栈

    if(cur!=nullptr)
    {
        if(cur[0]=='x')
            xx=false;
        else if(cur[0]=='y')
            yy=false;
    }
    if(cur==nullptr)
    {
        if(in=="x")
            cur=cur+in+"=";
        if(in=="y")
            cur=cur+in+"=";
    }
    //检查是否需要在数字或函数名之前插入一个乘号
    else if(num.contains(in))//数字
    {
        if(num1.contains(tail))//如  Π*2
            cur=cur+"*"+in;
        else
            cur+=in;
    }
    else if(in==".")//小数点
    {
        if(num.contains(tail))
        {
            bool f=true;
            for(auto i=cur.rbegin();i!=cur.rend();++i)//从后往前遍历，遍历该数字中是否还含有小数点
            {
                if(*i=="."){
                    f=false;
                    break;
                }
                if(!num.contains(*i))
                    break;
            }
            if (f)
                cur += ".";
            else
                cur = cur;
        }
    }
    else if(opt.contains(in))//运算符
    {
        if(in=="-")//单独考虑减号
        {
            if(tail.isEmpty()||tail=="("||tail=="=")
                cur+=in;
            else if(tail=="+")
                *(cur.end()-1) = '-';//返回一个指向容器的末尾元素下一个位置,+替换成-
            else if(num.contains(tail)||num1.contains(tail))
                cur+=in;
        }
        else if(num.contains(tail)||num1.contains(tail))//其他运算符
                cur+=in;
    }
    else if(func.contains(in)||xy.contains(in))//其他输入
    {
        if(num1.contains(tail)||num.contains(tail))
        {
            if(in=="x")
            {
                if(xx)
                    cur=cur+"*"+in;
            }
            else if(in=="y")
            {
                if(yy)
                    cur=cur+"*"+in;
            }
            else cur=cur+"*"+in;
        }
        else
        {
            if(in=="x")
            {
                if(xx)
                    cur=cur+in;
            }
            else if(in=="y")
            {
                if(yy)
                    cur=cur+in;
            }
            else cur=cur+in;
        }
    }
    else if(in=='(')// 右括号
    {
        if(num.contains(tail)||num1.contains(tail))
            cur=cur+"*"+in;
        else
            cur+='(';
        brackets++;
    }
    else if(in==')')//左括号
    {
        if(brackets>0&&(num.contains(tail)||num1.contains(tail)))
        {
            cur+=')';
            --brackets;
        }
    }
    ui->input->setText(cur);
}

void Inputfunc::btnClick()
{
    auto expr=ui->input->text().toStdString();//文本转换为标准字符串
    if(brackets==0&&expr!="")
    {
        std::vector<std::any> post;//储存合法的逆波兰表达式

        if(!Analyse::getPostfix(expr,post))//转化为逆波兰表达式并检查是否合法
            QMessageBox::information(this,QString("提示"),QString("函数表达式有错误"));
        else
        {
            emit onOK(expr,post);
            this->close();
        }
    }
    this->close();
}
